package com.yoj.questionservice.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yoj.questionservice.mapper.QuestionSubmitMapper;
import com.yoj.questionservice.service.QuestionService;
import com.yoj.questionservice.service.QuestionSubmitService;
import com.yoj.common.common.ResultCodeEnum;
import com.yoj.common.constant.CommonConstant;
import com.yoj.common.constant.MqConstant;
import com.yoj.common.exception.BusinessException;
import com.yoj.common.utils.SqlUtils;
import com.yoj.model.dto.questionsubmit.QuestionSubmitAddRequest;
import com.yoj.model.dto.questionsubmit.QuestionSubmitQueryRequest;
import com.yoj.model.entity.Question;
import com.yoj.model.entity.QuestionSubmit;
import com.yoj.model.entity.User;
import com.yoj.model.enums.QuestionSubmitLanguageEnum;
import com.yoj.model.enums.QuestionSubmitStatusEnum;
import com.yoj.model.vo.QuestionSubmitVO;
import com.yoj.model.vo.QuestionVO;
import com.yoj.questionservice.rabbitmq.RabbitMqSender;
import com.yoj.serviceclient.service.user.UserFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

/**
 * question_submit 题目提交表的数据库操作 Service 实现
 */
@Slf4j
@Service
public class QuestionSubmitServiceImpl extends ServiceImpl<QuestionSubmitMapper, QuestionSubmit>
        implements QuestionSubmitService {

    @Lazy
    @Resource
    private QuestionService questionService;

    @Resource
    private UserFeignClient userFeignClient;

    @Resource
    private RabbitMqSender rabbitMqSender;

    /**
     * 检验题目提交信息是否合法
     */
    public void validateQuestionSubmitAdd(QuestionSubmitAddRequest questionSubmitAddRequest) {
        // 检验编程语言是否合法
        String language = questionSubmitAddRequest.getLanguage();
        // 判断提交的编程语言是否合法，要在后端支持的编程语言类型内
        QuestionSubmitLanguageEnum languageEnum = QuestionSubmitLanguageEnum.getEnumByValue(language);
        if (languageEnum == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR, "编程语言错误");
        }
        Long questionId = questionSubmitAddRequest.getQuestionId();
        // 判断提交答案的题目是否存在
        Question question = questionService.getById(questionId);
        if (question == null) {
            throw new BusinessException(ResultCodeEnum.NOT_FOUND_ERROR);
        }
    }

    /**
     * 提交题目
     * @param questionSubmitAddRequest 包含提交题目所需信息的请求对象
     * @param loginUser                当前进行提交操作的用户信息
     * @return 返回提交的题目ID
     * @throws BusinessException 如果编程语言不合法、题目不存在或数据保存失败时，抛出业务异常。
     */
    @Override
    public long doQuestionSubmit(QuestionSubmitAddRequest questionSubmitAddRequest, User loginUser) {
        // 检验题目提交信息是否合法
        validateQuestionSubmitAdd(questionSubmitAddRequest);
        // 查询提交题目
        Long questionId = questionSubmitAddRequest.getQuestionId();
        Question question = questionService.getById(questionId);
        // 封装题目提交信息
        QuestionSubmit questionSubmit = new QuestionSubmit()
                .setUserId(loginUser.getId())
                .setQuestionId(questionId)
                .setStatus(QuestionSubmitStatusEnum.WAITTING.getValue()) // 设置初始状态：等待中
                .setCode(questionSubmitAddRequest.getCode())
                .setLanguage(questionSubmitAddRequest.getLanguage())
                .setJudgeInfo("{}");
        // 保存题目提交信息
        boolean save = this.save(questionSubmit);
        if (!save) {
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR, "数据插入失败");
        }

        // 更新该题目的提交数，加锁保证线程安全
        synchronized (this) {
            Question updateQuestion = new Question()
                    .setId(questionId)
                    .setSubmitNum(question.getSubmitNum() + 1);
            boolean updateById = questionService.updateById(updateQuestion);
            if (!updateById) {
                throw new BusinessException(ResultCodeEnum.OPERATION_ERROR, "问题提交数据保存失败");
            }
        }

        // 消息队列解耦判题服务，发送判题消息给判题服务进行判题
        Long questionSubmitId = questionSubmit.getId();
        rabbitMqSender.sendMessage(MqConstant.JUDGE_EXCHANGE_NAME, MqConstant.JUDGE_ROUTING_KEY, String.valueOf(questionSubmitId));
        // log.info("题目服务发送判题信息至消息队列，执行完毕！");
        log.info("用户 {} 提交了题目 {}，提交 ID 为 {}，消息已发送至判题服务的消息队列进行判题", loginUser.getId(), questionId, questionSubmitId);
        return questionSubmitId;
    }

    /**
     * 获取查询包装类
     * 用户根据哪些字段查询，根据前端传来的请求对象，得到 mybatis 框架支持的查询 QueryWrapper 类
     */
    @Override
    public QueryWrapper<QuestionSubmit> getQueryWrapper(QuestionSubmitQueryRequest questionSubmitQueryRequest) {
        QueryWrapper<QuestionSubmit> queryWrapper = new QueryWrapper<>();
        if (questionSubmitQueryRequest == null) {
            return queryWrapper;
        }
        String language = questionSubmitQueryRequest.getLanguage();
        Integer status = questionSubmitQueryRequest.getStatus();
        Long questionId = questionSubmitQueryRequest.getQuestionId();
        Long userId = questionSubmitQueryRequest.getUserId();

        // QuestionSubmitQueryRequest 需要继承 PageRequest
        // 获取排序字段
        String sortField = questionSubmitQueryRequest.getSortField();
        // 获取排序方向
        String sortOrder = questionSubmitQueryRequest.getSortOrder();

        // 拼接查询条件 PageRequest
        queryWrapper.eq(StringUtils.isNotBlank(language), "language", language);
        queryWrapper.eq(QuestionSubmitStatusEnum.getEnumByValue(status) != null, "status", status);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionId), "questionId", questionId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        // 设置排序条件
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);

        return queryWrapper;
    }

    /**
     * 查询单个题目封装信息
     */
    @Override
    public QuestionSubmitVO getQuestionSubmitVO(QuestionSubmit questionSubmit, User loginUser) {
        QuestionSubmitVO questionSubmitVo = QuestionSubmitVO.objToVo(questionSubmit);
        // 关联题目和用户信息
        Question question = questionService.getById(questionSubmit.getQuestionId());
        questionSubmitVo.setQuestionVO(QuestionVO.objToVo(question));
        questionSubmitVo.setUserVO(userFeignClient.getUserVO(loginUser));
        // 脱敏：仅本人和管理员能看见自己（提交 userId 和登录用户 id 不同）提交的代码
        if (!questionSubmitVo.getUserId().equals(loginUser.getId()) && userFeignClient.isAdmin(loginUser)) {
            questionSubmitVo.setCode(null);
        }
        return questionSubmitVo;
    }

    /**
     * 分页查询题目提交视图
     */
    @Override
    public Page<QuestionSubmitVO> getQuestionSubmitVOPage(Page<QuestionSubmit> questionSubmitPage, User loginUser) {
        // 题目提交列表
        List<QuestionSubmit> questionSubmitList = questionSubmitPage.getRecords();
        Page<QuestionSubmitVO> questionSubmitVOPage = new Page<>(questionSubmitPage.getCurrent(), questionSubmitPage.getSize(), questionSubmitPage.getTotal());
        if (CollUtil.isEmpty(questionSubmitList)) {
            return questionSubmitVOPage;
        }
        // 对题目提交列表中的提交信息进行脱敏处理
        List<QuestionSubmitVO> questionSubmitVOList = questionSubmitList.stream()
                .map(questionSubmit -> getQuestionSubmitVO(questionSubmit, loginUser))
                .collect(Collectors.toList());
        questionSubmitVOPage.setRecords(questionSubmitVOList);
        return questionSubmitVOPage;
    }

    /**
     * 分页查询题目提交视图
     */
    @Override
    public Page<QuestionSubmitVO> getQuestionSubmitVOPage(QuestionSubmitQueryRequest questionSubmitQueryRequest, HttpServletRequest request) {
        // 从数据库中查询原始的题目提交分页信息
        Page<QuestionSubmit> questionSubmitPage = this.page(new Page<>(questionSubmitQueryRequest.getCurrent(), questionSubmitQueryRequest.getPageSize()),
                getQueryWrapper(questionSubmitQueryRequest));
        final User loginUser = userFeignClient.getLoginUser(request);
        // 返回脱敏信息，需要从 request 中获取登录用户做权限校验
        return getQuestionSubmitVOPage(questionSubmitPage, loginUser);
    }

    /**
     * 分页获取题目提交封装信息（用户查询自己）
     */
    @Override
    public Page<QuestionSubmitVO> getUserQuestionSubmitVOPage(QuestionSubmitQueryRequest questionSubmitQueryRequest, HttpServletRequest request) {
        long current = questionSubmitQueryRequest.getCurrent();
        long size = questionSubmitQueryRequest.getPageSize();
        User loginUser = userFeignClient.getLoginUser(request);
        questionSubmitQueryRequest.setUserId(loginUser.getId());
        // 分页查询
        Page<QuestionSubmit> questionSubmitPage = this.page(new Page<>(current, size),
                getQueryWrapper(questionSubmitQueryRequest));
        // 获取题目提交分页信息包装类
        return getQuestionSubmitVOPage(questionSubmitPage, loginUser);
    }

    /**
     * 根据题目提交 id 查询判题结果
     */
    @Override
    public QuestionSubmitVO getQuestionSubmitVO(Long id, HttpServletRequest request) {
        QuestionSubmit questionSubmit = this.getById(id);
        if (questionSubmit == null) {
            throw new BusinessException(ResultCodeEnum.NOT_FOUND_ERROR, "提交答案不存在");
        }
        User loginUser = userFeignClient.getLoginUser(request);
        QuestionSubmitVO questionSubmitVO = getQuestionSubmitVO(questionSubmit, loginUser);
        return questionSubmitVO;
    }

}